﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Collections;
using Valentia.Kinection.DataTypes;
using Valentia.Kinection.Remoting.Proxy;

namespace Valentia.Kinection.Remoting.ClientTest
{
    public class _RemotingClient
    {
        #region For Remoting Client Data Passing Events

        /// <summary>
        /// Delegate for devices connection information to be sent.
        /// </summary>
        public delegate void DeviceConnectivityInfoEventDlg(List<KinectDeviceInfo> deviceConnectionInfo);

        /// <summary>
        /// Delegate for clients connection information to be sent.
        /// </summary>
        public delegate void ClientConnectivityInfoEventDlg(List<ServiceClientInfo> clientConnectionInfo);

        /// <summary>
        /// This event will be fired when server needs to send information of devices connection to client.
        /// </summary>
        public event DeviceConnectivityInfoEventDlg DeviceConnectivityInfoEvent;

        /// <summary>
        /// This event will be fired when server needs to send information of client connected to service.
        /// </summary>
        public event ClientConnectivityInfoEventDlg ClientConnectivityInfoEvent;

        #endregion

        IRemotingServerObject remoteServer;
        RemotingProxy eventProxy;
        TcpChannel tcpChan;
        BinaryClientFormatterSinkProvider clientProv;
        BinaryServerFormatterSinkProvider serverProv; 

        private string serverURI = "tcp://localhost:15000/serverExample.Rem";        //Replace with your IP

        private bool connected = false;

        public _RemotingClient()
        {
            clientProv = new BinaryClientFormatterSinkProvider();
            serverProv = new BinaryServerFormatterSinkProvider();
            serverProv.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;

            eventProxy = new RemotingProxy();
            eventProxy.KinectDeviceInfoReceived += new DeviceConnectivityInfo(eventProxy_DeviceConnectivityInfo);
            eventProxy.ServiceClientInfoReceived += new ClientConnectivityInfo(eventProxy_ClientConnectivityInfo);

            Hashtable props = new Hashtable();
            props["name"] = "remotingClient";
            props["port"] = 0;      //First available port

            tcpChan = new TcpChannel(props, clientProv, serverProv);

            ChannelServices.RegisterChannel(tcpChan);

            RemotingConfiguration.RegisterWellKnownClientType(new WellKnownClientTypeEntry(typeof(IRemotingServerObject)
                , serverURI));
        }

        public void Connect()
        {
            if (connected)
                throw new Exception("Already connected with server");

            try
            {
                if (!ChannelServices.RegisteredChannels.Contains(tcpChan))
                {
                    
                    ChannelServices.RegisterChannel(tcpChan);

                    RemotingConfiguration.RegisterWellKnownClientType(new WellKnownClientTypeEntry(typeof(IRemotingServerObject)
                        , serverURI));
                }

                remoteServer = (IRemotingServerObject)Activator.GetObject(typeof(IRemotingServerObject), serverURI);
                remoteServer.CommandFromClient(ClientCommands.ClientConnected, null, null);        //This is where it will break if we didn't connect

                //Now we have to attach the events...
                remoteServer.KinectDeviceInfoReceived
                    += new DeviceConnectivityInfo(eventProxy.HandleDeviceConnectionInfoEvent);

                remoteServer.ServiceClientInfoReceived
                    += new ClientConnectivityInfo(eventProxy.HandleClientConnectionInfoEvent);

                connected = true;
            }
            catch (Exception ex)
            {
                connected = false;
                throw ex;
            }
        }

        public void Disconnect()
        {
            try
            {
                if (!connected)
                    throw new Exception("Not connected with server");

                //First remove the event
                remoteServer.KinectDeviceInfoReceived -= eventProxy.HandleDeviceConnectionInfoEvent;

                remoteServer.ServiceClientInfoReceived -= eventProxy.HandleClientConnectionInfoEvent;

                //Now we can close it out
                ChannelServices.UnregisterChannel(tcpChan);

                connected = false;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        public void SendCommand(ClientCommands command, String loginName, String password)
        {
            try
            {
                if (!connected)
                    throw new Exception("Not connected with server");

                remoteServer.CommandFromClient(command, loginName, password);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        void eventProxy_ClientConnectivityInfo(List<ServiceClientInfo> clientConnectionInfo, String errorMessage)
        {
            if (ClientConnectivityInfoEvent != null)
            {
                ClientConnectivityInfoEvent(clientConnectionInfo);
            }
        }

        void eventProxy_DeviceConnectivityInfo(List<KinectDeviceInfo> deviceConnectionInfo, String errorMessage)
        {
            if (DeviceConnectivityInfoEvent != null)
            {
                DeviceConnectivityInfoEvent(deviceConnectionInfo);
            }
        }
    }
}
